#!/user/bin/env set PYTHONIOENCODING=utf-8 && python -u
# -*- coding: utf-8 -*-

# import cbor2
import struct
import time
import random

import cbor2

from .primitives import Primitives
from .settings import Settings

from pycose import headers
from pycose.algorithms import AESCCM1664128
from pycose.messages import Enc0Message
from pycose.keys import SymmetricKey

settings = Settings()
encrypt0_aes_ccm_128_key = bytes(settings['codec.cbor2.t_series.cose.encrypt0_aes_ccm_128_key'])
encrypt0_aes_ccm_128_iv = bytes(settings['codec.cbor2.t_series.cose.encrypt0_aes_ccm_128_iv'])
encrypt0_iv_length = settings['codec.cbor2.t_series.cose.encrypt0_iv_length']
cbor2_t_series_encode_fields = settings['codec.cbor2.t_series.fields']
cbor2_t_series_decode_fields = { value: key for key, value in cbor2_t_series_encode_fields.items() }

class CoseSerializer:

  def dumps_request(self, method, request, id=0):
    result = dict(topic=method)

    CoseEncoder.encode_with_aes_ccm_16_64_128(request['payload'])
    result.update(request)
    return result

  def loads_response(self, response):
    return [ response ]

class CoseEncoder:

  @staticmethod
  def encode_with_aes_ccm_16_64_128(payload):
    cbor2_message = None
    if Primitives.is_dict(payload):
      cbor2_message = cbor2.dumps({ cbor2_t_series_encode_fields[key]: value for key, value in payload.items() })
    elif Primitives.is_bytes(payload):
      cbor2_message = payload
    else:
      raise TypeError('payload should be of type "dict" or "bytes"')

    iv = encrypt0_aes_ccm_128_iv + struct.pack('>i', int(time.time())) +\
         random.randbytes(encrypt0_iv_length - len(encrypt0_aes_ccm_128_iv) - 4)
    encrypt0_message = Enc0Message(phdr={ headers.Algorithm: AESCCM1664128, headers.IV: iv },
                                   key=SymmetricKey(k=encrypt0_aes_ccm_128_key), payload=cbor2_message)
    rgb_encrypt = encrypt0_message.encrypt()
    encrypt0_message.phdr_update({ headers.IV: iv.lstrip(encrypt0_aes_ccm_128_iv) })
    message = [encrypt0_message.phdr_encoded, encrypt0_message.uhdr_encoded, rgb_encrypt]
    result = cbor2.dumps(message, default=encrypt0_message._custom_cbor_encoder)
    return result

class CoseDecoder:

  @staticmethod
  def decode_with_aes_ccm_16_64_128(payload):
    if not Primitives.is_bytes(payload):
      raise Exception('payload should be of type "bytes"')
      # random.randbytes(4)


    cbor_message = cbor2.loads(payload)
    enc0_message = Enc0Message.from_cose_obj(cbor_message, True)
    enc0_message.phdr_update({ headers.IV: encrypt0_aes_ccm_128_iv + enc0_message._get_nonce() })
    enc0_message.key = SymmetricKey(k=encrypt0_aes_ccm_128_key)
    rgb_decrypt = enc0_message.decrypt()
    result = cbor2.loads(rgb_decrypt)
    return { cbor2_t_series_decode_fields[key]: value for key, value in result.items() }
